#include <iostream>
#include <cstring>
#include <unistd.h>

#include "HTTPConnection.h"

HTTPConnection::HTTPConnection() : client_fd(-1), client_address({0}), is_close(false), is_keep_alive(false),
                                   iov_count(0), iov(2, {nullptr}),
                                   read_buffer(), write_buffer(),
                                   request(std::make_unique<HTTPRequest>()),
                                   response(std::make_unique<HTTPResponse>()) {}

HTTPConnection::HTTPConnection(int fd, const struct sockaddr_in &client)
        : client_fd(fd), client_address(client), is_close(false), is_keep_alive(false),
          iov_count(0), iov(2, {nullptr}),
          read_buffer(), write_buffer(),
          request(std::make_unique<HTTPRequest>()), response(std::make_unique<HTTPResponse>()) {
//    ++(HTTPConnection::connections_count);
}


HTTPConnection::~HTTPConnection() {
    if (!this->is_close) {
        this->is_close = true;
//        --(HTTPConnection::connections_count);
        close(this->client_fd);
    }
}

//HTTPConnection::HTTPConnection(HTTPConnection &&other) noexcept
//        : client_fd(other.client_fd), client_address(other.client_address), is_close(false), is_keep_alive(false),
//          iov_count(other.iov_count), iov(other.iov),
//          read_buffer(other.read_buffer), write_buffer(other.write_buffer) {
//    other.client_fd = 0;
//    other.client_address = {0};
//    other.iov_count = 0;
//    other.iov.clear();
//    other.read_buffer.clear();
//    other.write_buffer.clear();
//    ++(HTTPConnection::connections_count);
//}

void HTTPConnection::init_connection(int fd, const sockaddr_in &client) {
//    ++(HTTPConnection::connections_count);
    this->client_fd = fd;
    this->client_address = client;
    this->is_close = false;
    this->is_keep_alive = false;
    iov_count = 0;
    iov = {};
    this->read_buffer;
    this->write_buffer;
    this->request = std::make_unique<HTTPRequest>();
    this->response = std::make_unique<HTTPResponse>();
}

int HTTPConnection::get_client_fd() const {
    return this->client_fd;
}

struct sockaddr_in HTTPConnection::get_client_address() const {
    return this->client_address;
}

std::string HTTPConnection::get_client_ip() const {
    std::string temp(inet_ntoa(this->client_address.sin_addr));
    return temp;
}

uint16_t HTTPConnection::get_client_port() const {
    return this->client_address.sin_port;
}

// 客户端连接中处理读事件的接口
bool HTTPConnection::read(int &error) {
    int socket_fd = this->client_fd;
    char buff[2048];
    this->read_buffer.clear();
    // 没有写设置模式LT还是ET。默认以ET模式，所以要循环一次性读完
    while (true) {
        std::memset(buff, 0, sizeof(buff));
        ssize_t bytes_read = recv(socket_fd, buff, sizeof(buff), MSG_WAITALL);  // TODO: flag有疑问
        if (bytes_read > 0) {
            this->read_buffer.append(buff);
        } else if (bytes_read == -1 && errno == EINTR) {  // 程序正常中断，继续读取
            std::cout << "reading continue...\n";
            continue;
        } else if (bytes_read == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {  // 非阻塞IO，这个条件表示数据全部读取完毕
            error = EAGAIN;
            break;
        } else if (bytes_read == 0) {  // EOF，客户端断开连接
            std::cout << "client fd " << socket_fd << " is disconnected.\n";
            close(socket_fd);
            this->is_close = true;
            return false;
        } else {
            std::cout << "Other error on client fd " << client_fd << std::endl;
            close(socket_fd);
            return false;
        }
    }

    return true;
}

// 客户端连接中处理写事件的接口
bool HTTPConnection::write(int &error) {
    int socket_fd = this->client_fd;
#if 0
    // writer_buffer应有响应后的数据
    char buffer[this->write_buffer->get_size()];
    std::memcpy(buffer, this->write_buffer->get_c_string(), this->write_buffer->get_size());
    unsigned data_size = this->write_buffer->get_size();
    int data_left = data_size;

    while (data_left > 0) {
        ssize_t bytes_write = send(socket_fd, buffer + data_size - data_left, data_left, 0);  // TODO: flag有疑问
        if (bytes_write == -1 && errno == EINTR) {
            std::cout << "write continue...\n";
            continue;
        } else if (bytes_write == -1 && errno == EAGAIN) {
            return true;
        } else if (bytes_write == -1) {
            return false;
        }
        data_left -= bytes_write;
    }
    std::cout << this->write_buffer->get_c_string();
    this->write_buffer->clear();
#endif
    size_t bytes_left = this->iov[0].iov_len + this->iov[1].iov_len;
    auto iov_ptr = iov.data();
    while (bytes_left > 0) {
        size_t bytes_write = writev(socket_fd, iov_ptr, iov_count);
        if (bytes_write == -1 && errno == EINTR) {
            continue;
        } else if (bytes_write == -1 && errno == EAGAIN) {  // 缓冲区满，应设置EPOLLOUT等待下一次读事件
            error = EAGAIN;
            break;
        } else if (bytes_write == -1) {
            return false;
        }
        // 更新iovec数组
        while (bytes_write > 0) {
            if (bytes_write >= iov[0].iov_len) {
                bytes_write -= iov[0].iov_len;
                bytes_left -= iov[0].iov_len;
                ++iov_ptr;
                --iov_count;
            } else {
                iov[0].iov_base = (char *) iov[0].iov_base + bytes_write;
                iov[0].iov_len -= bytes_write;
                bytes_left -= bytes_write;
                bytes_write = 0;
            }
        }
    }

    return true;
}

// 解析HTTP连接，根据请求生成对应的响应
void HTTPConnection::parse_connection() {
    this->request->pares_request(this->read_buffer);
    this->response->generate_response(*this->request, this->write_buffer);
    this->is_keep_alive = this->request->is_keep_alive();
    iov[0].iov_base = const_cast<char *>(this->write_buffer.get_c_string());
    iov[0].iov_len = this->write_buffer.get_size();
    iov_count = 1;

    if (this->response->get_file() && this->response->get_file_length() > 0) {
        iov[1].iov_base = this->response->get_file();
        iov[1].iov_len = this->response->get_file_length();
        iov_count = 2;
    }
}

void HTTPConnection::set_writer_buffer(const char *string) {
    write_buffer.set_buffer(string);
}

bool HTTPConnection::get_is_keep_alive() const {
    return is_keep_alive;
}
